#include "pcap.h"
#include "stdio.h"
#include "stdlib.h"
#include "unistd.h"
#include "iostream"
#include "sys/stat.h"
#include "sys/types.h"
#include "stdint.h"
#include "string.h"
#include "syslog.h"
#include "string.h"

#include "arpa/inet.h"
#include "linux/if_ether.h"
#include "linux/ip.h"
#include "linux/tcp.h"
#include "linux/udp.h"
#include "save_packet.h"

/* macro*/
#define READ_SIZE 128
#define likely(x)  __builtin_expect(!!(x), 1)
#define unlikely(x)    __builtin_expect(!!(x), 0)

/* structs */
struct toption{
	uint8_t	kind;	//tcp option kind
	uint8_t	len;	//option length
	uint8_t *info;	//info
};
struct utcphdr{
	struct tcphdr	*tcphd;
	struct toption	*option;
};
struct hdr{
	struct ethhdr	*ethhd;
	struct iphdr	*iphd;
	union{
		struct utcphdr	*utcphd;
		struct udphdr	*udphd;
	}tuhd;
};

using namespace std;

/* chechsum */
uint16_t checksum(uint16_t *data, int num)
{
	uint32_t sum = 0;
	uint16_t check = 0;
	int index = 0;
	for(; index < num; index++)
	{
		sum += data[index];
		check = sum;
		if(sum & 0x10000)
			check++;
		sum = 0x01111;
	}
	return ~check;
}

/* get net packet header*/
/* ntohl may need to be called when use*/
int get_header(void *pkt, int len, struct hdr *head)
{
	uint8_t *data = (uint8_t*)pkt;
	
	head->ethhd = new ethhdr;
	// only ipv4 here
	head->iphd = new iphdr;

	int hlen = 0;	// header length
	/* skip broadcast packet*/
	if (unlikely(0 == memcmp(data, "\xff\xff\xff\xff\xff\xff", 6))) 
	{
		return 1;
	}
	/* ETH_II*/
	//host len
	//ARP 0x0806
	//IPV4 0x0800
	head->ethhd = (struct ethhdr*)data;
	if(head->ethhd->h_proto != 0x08)
		return 1;
	data += 14;
	len -= 14;
	
	/* ipv4*/
	head->iphd = (struct iphdr*)data;
	hlen = head->iphd->ihl * 4;
	data += hlen;
	len -= hlen;
	
	/* tcp or udp*/
	switch(head->iphd->protocol)
	{
		case 6:		//tcp
			head->tuhd.utcphd = new utcphdr;
			head->tuhd.utcphd->tcphd = (struct tcphdr*)data;
			hlen = head->tuhd.utcphd->tcphd->doff * 4;
			if(hlen > 20)	//sizeof(struct tcphdr) = 20
			{	//tcp option
				head->tuhd.utcphd->option = (struct toption*)data;
			}
			data += hlen;
			len -= len;
			break;
		case 17:	//udp
			head->tuhd.udphd = new udphdr;
			head->tuhd.udphd = (struct udphdr*)data;
			data += 8;
			len -= 8;
			break;
		default:
			//icmp	1
			//
			break;
	}
	
	/* return */
	return 0;
}

void getPacket(u_char * arg, const struct pcap_pkthdr * pkthdr, const u_char * packet)
{
	int * id = (int *)arg;

	printf("id: %d\n", ++(*id));
	printf("Packet length: %d\n", pkthdr->len);
	printf("Number of bytes: %d\n", pkthdr->caplen);
	printf("Recieved time: %s", ctime((const time_t *)&pkthdr->ts.tv_sec));

	unsigned int i;
	for(i=0; i<pkthdr->len; ++i)
	{
		printf(" %02x ", packet[i]);
		if( (i + 1) % 16 == 0 )
		{
			printf("\n");
		}
	}
	save_packet(packet, pkthdr->caplen);
	printf("\n\n");
	if(*(id)>3)
		exit(0);
}

/* read local binary file and send them*/
// flag 0 don't show in stdout
// fd file decripter
int restream(const char *fname, int fd, int flag)
{
	const int size = 1;
	int count = 0, ret, pre = 0, i = 0;
	unsigned char buf[64];
	FILE *fp = fopen(fname, "rb");
	if(NULL == fp){
		cout<<"open file "<<fname<<" failed"<<endl;
		return 0;
	}

	if(flag){
		cout<<"Address\t  ";
		for(i = 0; i<16; i++) cout<<hex<<i<<"\t";
		printf("\n%08x  ", 0);
	}

	while(1){
		ret = fread(buf, size, 1, fp);
		count += size;

		if(flag && ret){
			for(i = 0; i < size; i++){
				if((pre + i)%16 == 0 && (pre + i) > 0)
					printf("\n%08x  ", int((pre + i)/16));
				printf("%02x\t", buf[i]);
			}
			pre = count;
		}

		if(feof(fp))
			return count;
	}

	return count;
}

int main(int argc, char **argv)
{
	char errBuf[PCAP_ERRBUF_SIZE], *devStr;
	struct sockaddr_in sin;
	int ret;

	/* get a device */
	devStr = pcap_lookupdev(errBuf);

	if(devStr)
	{
		printf("success: device: %s\n", devStr);
	}
	else
	{
		printf("error: %s\n", errBuf);
		exit(1);
	}

	/* open a device, wait until a packet arrives */
	pcap_t * device = pcap_open_live(devStr, 65535, 1, 0, errBuf);

	if(!device)
	{
		printf("error: pcap_open_live(): %s\n", errBuf);
		exit(1);
	}

	/* wait loop forever */
	//int id = 0;
	//pcap_loop(device, -1, getPacket, (u_char*)&id);
	//
	//pcap_close(device);
	
	int sockfd = 0;
	sockfd = socket(AF_INET, SOCK_STREAM, 0);
	if(sockfd < 0)
	{
		printf("error: socket(): %s\n", strerror(sockfd));
		return 1;
	}
	sin.sin_family = AF_INET;
	sin.sin_port = htons(9000);

	ret = inet_pton(AF_INET, "127.0.0.1", &(sin.sin_addr.s_addr));
	if(1 != ret)
	{
		printf("error:	inet_pton(): error code %d\n", ret);
		return 1;
	}
	
	ret = connect(sockfd, (struct sockaddr*)&sin, sizeof(sin));
	if(ret){
		cout<<"error: connect()"<<endl;
		exit(1);
	}

	cout<<"connected"<<endl;

	const char *buf = "hello world";
	ret = write(sockfd, buf, strlen(buf));

	cout<<"write buf: "<<buf<<", "<<ret<<endl;

	restream("./packet_1487652188.pcap", 0, 1);
	return 0;
}
